/* 
fastsprite
Copyright (c) 2011 Phillip Nguyen.
All rights reserved.
*/

#include <Python.h>
#include <math.h>
#include "structmember.h"

#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif

typedef struct {
    PyObject_HEAD
    int texture_id;          // OpenGL texture ID
    float x, y;              // sprite position
    float x1, y1, x2, y2;    // relative sprite shape
    float u1, v1, u2, v2;    // texture coordinates
    float rot;               // rotation in degrees
    float scale;             // scale factor
} Sprite;

static PyMemberDef Sprite_members[] = {
    {"texture_id", T_INT, offsetof(Sprite, texture_id), 0, "Sprite OpenGL texture ID"},
    {"x", T_FLOAT, offsetof(Sprite, x), 0, "Sprite x position"},
    {"y", T_FLOAT, offsetof(Sprite, y), 0, "Sprite y position"},
    {"x1", T_FLOAT, offsetof(Sprite, x1), 0, "Sprite left position"},
    {"y1", T_FLOAT, offsetof(Sprite, y1), 0, "Sprite bottom position"},
    {"x2", T_FLOAT, offsetof(Sprite, x2), 0, "Sprite right position"},
    {"y2", T_FLOAT, offsetof(Sprite, y2), 0, "Sprite top position"},
    {"u1", T_FLOAT, offsetof(Sprite, u1), 0, "Sprite left texture coordinate"},
    {"v1", T_FLOAT, offsetof(Sprite, v1), 0, "Sprite bottom texture coordinate"},
    {"u2", T_FLOAT, offsetof(Sprite, u2), 0, "Sprite right texture coordinate"},
    {"v2", T_FLOAT, offsetof(Sprite, v2), 0, "Sprite top texture coordinate"},
    {"rot", T_FLOAT, offsetof(Sprite, rot), 0, "Sprite rotation in degrees"},
    {"scale", T_FLOAT, offsetof(Sprite, scale), 0, "Sprite scaling factor"},
    {NULL}
};

static PyObject *
Sprite_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    Sprite *self;
    
    self = (Sprite *)type->tp_alloc(type, 0);
    if (self != NULL) {
	self->texture_id = 0;

	self->x = 0;
	self->y = 0;

	self->x1 = -10;
	self->y1 = -10;
	self->x2 = 10;
	self->y2 = 10;

	self->u1 = 0;
	self->v1 = 0;
	self->u2 = 1;
	self->v2 = 1;

	self->rot = 0;
	self->scale = 1;
    }

    return (PyObject *)self;
}

static int
Sprite_init(Sprite *self, PyObject *args, PyObject *kwds)
{
    static char *kwlist[] = {"texture_id", "x", "y", 
			     "x1", "y1", "x2", "y2", 
			     "u1", "v1", "u2", "v2",
			     "rot", "scale", NULL};
    
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iffffffffff", kwlist, &self->texture_id, 
				     &self->x1, &self->y1, &self->x2, &self->y2,
				     &self->u1, &self->v1, &self->u2, &self->v2,
				     &self->rot, &self->scale))
	return -1;
    
    return 0;
}


static PyObject *
Sprite_draw(Sprite* self)
{
    if (self->texture_id != 0) {
	glBindTexture(GL_TEXTURE_2D, self->texture_id);
	
	float x = self->x;
	float y = self->y;

	float x1 = self->x1, y1 = self->y1;
	float x2 = self->x2, y2 = self->y2;

	if (self->scale != 1.0) {
	    x1 *= self->scale;
	    y1 *= self->scale;
	    x2 *= self->scale;
	    y2 *= self->scale;
	}

	glBegin(GL_QUADS);
	if (self->rot == 0.0) {
	    glTexCoord2f(self->u1, self->v1);
	    glVertex2f(x + x1, y + y1);
	    glTexCoord2f(self->u2, self->v1);
	    glVertex2f(x + x2, y + y1);
	    glTexCoord2f(self->u2, self->v2);
	    glVertex2f(x + x2, y + y2);
	    glTexCoord2f(self->u1, self->v2);
	    glVertex2f(x + x1, y + y2);
	} else {
	    float rad = self->rot*0.01745329252f;
	    float c = cos(rad), s = sin(rad);
	    float x1c = x1*c, x1s = x1*s;
	    float y1c = y1*c, y1s = y1*s;
	    float x2c = x2*c, x2s = x2*s;
	    float y2c = y2*c, y2s = y2*s;

	    glTexCoord2f(self->u1, self->v1);
	    glVertex2f(x + x1c - y1s, y + x1s + y1c);
	    glTexCoord2f(self->u2, self->v1);
	    glVertex2f(x + x2c - y1s, y + x2s + y1c);
	    glTexCoord2f(self->u2, self->v2);
	    glVertex2f(x + x2c - y2s, y + x2s + y2c);
	    glTexCoord2f(self->u1, self->v2);
	    glVertex2f(x + x1c - y2s, y + x1s + y2c);	    
	}
	glEnd();
    }

    Py_RETURN_NONE;
}


static PyMethodDef Sprite_methods[] = {
    {"draw", (PyCFunction)Sprite_draw, METH_NOARGS, "Draw the sprite."},
    {NULL}
};

static PyTypeObject SpriteType = {
    PyObject_HEAD_INIT(NULL)
    0,                            // ob_size
    "Sprite",                     // tp_name
    sizeof(Sprite),               // tp_basicsize
    0,                            // tp_itemsize
    0,                            // tp_dealloc
    0,                            // tp_print
    0,                            // tp_getattr
    0,                            // tp_setattr
    0,                            // tp_compare
    0,                            // tp_repr
    0,                            // tp_as_number
    0,                            // tp_as_sequence
    0,                            // tp_as_mapping
    0,                            // tp_hash
    0,                            // tp_call
    0,                            // tp_str
    0,                            // tp_getattro
    0,                            // tp_setattro
    0,                            // tp_as_buffer
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,           // tp_flags
    "Sprite objects",             // tp_doc
    0,                            // tp_traverse
    0,                            // tp_clear
    0,                            // tp_richcompare
    0,                            // tp_weaklistoffset
    0,                            // tp_iter
    0,                            // tp_iternext
    Sprite_methods,               // tp_methods
    Sprite_members,               // tp_members
    0,                            // tp_getset
    0,                            // tp_base
    0,                            // tp_dict
    0,                            // tp_descr_get
    0,                            // tp_descr_set
    0,                            // tp_dictoffset
    (initproc)Sprite_init,        // tp_init
    0,                            // tp_alloc
    Sprite_new,                   // tp_new
};


static PyMethodDef module_methods[] = {
    {NULL}
};

#ifndef PyMODINIT_FUNC  // declarations for DLL import/export
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
init_fastsprite(void) {
    PyObject* m;
    
    if (PyType_Ready(&SpriteType) < 0)
	return;
    
    m = Py_InitModule3("_fastsprite", module_methods, "sprite module for creating Sprite objects.");
    
    Py_INCREF(&SpriteType);
    PyModule_AddObject(m, "Sprite", (PyObject *)&SpriteType);
}
